package com.yuki.consumer.aop;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.yuki.api.annotation.SystemLog;
import com.yuki.api.common.IpInfoUtil;
import com.yuki.api.common.ThreadPoolUtil;
import com.yuki.api.model.UmsAdmin;
import com.yuki.api.model.UmsLog;
import com.yuki.api.service.ITokenService;
import com.yuki.api.service.IUmsLogService;
import com.yuki.api.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @description:
 * @author: Yuki Tadayoshi
 * @time: 2021/3/27 17:33
 */
@Aspect
@Component
@Slf4j
public class SystemLogAspect implements HandlerInterceptor {

    private static final ThreadLocal<Date> beginTimeThreadLocal = new NamedThreadLocal<>("ThreadLocal BeginTime");

    @Reference(
            version = "1.0.0",
            interfaceName = "com.yuki.api.service.IUserService",
            interfaceClass = IUserService.class,
            timeout = 120000
    )
    private IUserService service;


    @Reference(
            version = "1.0.0",
            interfaceName = "com.yuki.api.service.ITokenService",
            interfaceClass = ITokenService.class,
            timeout = 120000
    )
    private ITokenService tokenService;


    @Reference(
            version = "1.0.0",
            interfaceName = "com.yuki.api.service.IUMSLogService",
            interfaceClass = IUmsLogService.class,
            timeout = 120000
    )
    private IUmsLogService logService;

    //@Pointcut("@annotation(com.yuki.api.annotation.SystemLog)")
    @Pointcut("execution(* com.yuki.consumer.controller.*.*(..))")
    public void point() {
        System.out.println("ok");
    }

    @Before("point()")
    public void before(JoinPoint joinPoint) {
        Date beginTime = new Date();
        beginTimeThreadLocal.set(beginTime);
    }

    @AfterReturning("point()")
    public void after(JoinPoint joinPoint) {
        try {
            UmsLog log = new UmsLog();
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            Map<String, Object> map = getControllerMethodInf(joinPoint);
            if(map.get("description")!=null){
                String description = map.get("description").toString();
                log.setName(description);
                Integer type = Integer.parseInt(map.get("type").toString());
                log.setLogType(type);
            }
            Map<String, String[]> requestParams = request.getParameterMap();
            String token = request.getHeader("Authorization");
            if (token == null) {
                throw new RuntimeException("无token，请重新登录！");
            }
            token = token.split("@")[1];
            String userId = tokenService.getUserId(token);
            UmsAdmin user = service.findUserById(Long.parseLong(userId));
            if (!tokenService.checkSign(token, user.getPassword())) {
                throw new RuntimeException("token验证失败");
            }
            log.setRequestUrl(request.getRequestURI());
            log.setRequestType(request.getMethod());
            String objStr2 = JSON.toJSONString(requestParams).replace(",", "&&").replace("\"", "\'");
            log.setRequestParam(objStr2);
            log.setIp(IpInfoUtil.getIpAddr(request));
            log.setCreateTime(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
            log.setUpdateTime(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
            long beginTime = beginTimeThreadLocal.get().getTime();
            long endTime = System.currentTimeMillis();
            Long logCostTime = endTime - beginTime;
            log.setCostTime(logCostTime.intValue());
            final Random random = new Random();
            int userid = random.nextInt(100);
            log.setUserid(userid);

            ThreadPoolUtil.getPool().execute(new SaveLogThread(log, logService));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Map<String, Object> getControllerMethodInf(JoinPoint point) {
        Map<String, Object> map = new HashMap<>(16);
        String targetName = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        Object[] arguments = point.getArgs();
        try {
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String description = "";
            Integer type = null;
            for (Method method : methods) {
                if (!method.getName().equals(methodName)) {
                    continue;
                }
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length != arguments.length) {
                    continue;
                }
                if (method.getAnnotation(SystemLog.class) != null) {
                    description = method.getAnnotation(SystemLog.class).description();
                    type = method.getAnnotation(SystemLog.class).type().ordinal();
                    map.put("description", description);
                    map.put("type", type);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

}
